S11-12 Vue-项目:mr-vue3-ts-consult-patient2
[TOC]
Consult
TS类型
知识点:Enum
1、使用枚举类型定义问诊类型、问诊时间
// 问诊类型
export enum ConsultType {
/** 找医生 */
Doctor = 1,
/** 快速问诊 */
Fast = 2,
/** 开药问诊 */
Medication = 3
}
// 问诊时间,以1自增可以省略
export enum IllnessTime {
/** 一周内 */
Week = 1,
/** 一月内 */
Month,
/** 半年内 */
HalfYear,
/** 半年以上 */
More
}
2、定义问诊记录类型
import { ConsultType, IllnessTime } from '@/enums'
// 图片列表
export type Image = {
/** 图片ID */
id: string
/** 图片地址 */
url: string
}
// 问诊记录
export type Consult = {
/** 问诊记录ID */
id: string
/** 问诊类型 */
type: ConsultType
/** 快速问诊类型,0 普通 1 三甲 */
illnessType: 0 | 1
/** 科室ID */
depId: string
/** 疾病描述 */
illnessDesc: string
/** 疾病持续时间 */
illnessTime: IllnessTime
/** 是否就诊过,0 未就诊过 1 就诊过 */
consultFlag: 0 | 1
/** 图片数组 */
pictures: Image[]
/** 患者ID */
patientId: string
/** 优惠券ID */
couponId: string
}
// 问诊记录-全部可选
export type PartialConsult = Partial<Consult>
// Required 转换为全部必须 Partial 转换问全部可选 两个内置的泛型类型
问诊类型
定义问诊仓库
1、在 store/consult.ts 中定义 consultStore
2、在 store/index.ts 中统一导出consultStore
记录问诊类型
1、在 store/consult.ts 中定义 setType() 方法,记录问诊类型
2、在 home 组件调用 setType() 方法,记录问诊类型
问诊级别:consult-fast
路由规则
页面布局
1、HTML
<template>
<div class="consult-fast-page">
<cp-nav-bar title="极速问诊" right-text="问诊记录"></cp-nav-bar>
<div class="fast-logo">
<img class="img" src="@/assets/consult-fast.png" alt="" />
<p class="text"><span>20s</span> 快速匹配专业医生</p>
</div>
<div class="fast-type">
<router-link to="/consult/dep" class="item">
<cp-icon class="pic" name="consult-doctor"></cp-icon>
<div class="info">
<p>三甲图文问诊</p>
<p>三甲主治及以上级别医生</p>
</div>
<van-icon name="arrow"></van-icon>
</router-link>
<router-link to="/consult/dep" class="item">
<cp-icon class="pic" name="consult-message"></cp-icon>
<div class="info">
<p>普通图文问诊</p>
<p>二甲主治及以上级别医生</p>
</div>
<van-icon name="arrow"></van-icon>
</router-link>
</div>
</div>
</template>
2、样式
.consult-fast-page {
padding-top: 46px;
.fast-logo {
padding: 30px 0;
text-align: center;
.img {
width: 240px;
}
.text {
font-size: 16px;
margin-top: 10px;
> span {
color: var(--cp-primary);
}
}
}
.fast-type {
padding: 15px;
.item {
display: flex;
padding: 16px;
border-radius: 4px;
align-items: center;
margin-bottom: 16px;
border: 0.5px solid var(--cp-line);
}
.pic {
width: 40px;
height: 40px;
}
.info {
margin-left: 12px;
flex: 1;
> p:first-child {
font-size: 16px;
color: var(--cp-text1);
margin-bottom: 4px;
}
> p:last-child {
font-size: 13px;
color: var(--cp-tag);
}
}
.van-icon {
color: var(--cp-tip);
}
}
}
记录问诊级别
1、在 store/consult.ts 中定义 setIllnessType() 方法,记录问诊级别
2、在 consult-fast 组件调用 setIllnessType() 方法,记录问诊级别
问诊科室:consult-department
路由规则
页面布局
1、HTML
<template>
<div class="consult-dep-page">
<!-- 导航栏 -->
<cp-nav-bar title="选择科室" />
<div class="wrapper">
<!-- 一级科室 -->
<van-sidebar v-model="active">
<van-sidebar-item title="内科" />
<van-sidebar-item title="外科" />
<van-sidebar-item title="皮肤科" />
<van-sidebar-item title="骨科" />
</van-sidebar>
<!-- 二级科室 -->
<div class="sub-dep">
<router-link to="/consult/illness">科室一</router-link>
<router-link to="/consult/illness">科室二</router-link>
<router-link to="/consult/illness">科室三</router-link>
</div>
</div>
</div>
</template>
2、样式
.van-sidebar {
width: 114px;
&-item {
padding: 14px;
color: var(--cp-tag);
&--select {
color: var(--cp-main);
font-weight: normal;
&::before {
display: none;
}
}
}
}
.consult-dep-page {
padding-top: 46px;
.wrapper {
height: calc(100vh - 46px);
overflow: hidden;
display: flex;
.sub-dep {
flex: 1;
height: 100%;
overflow-y: auto;
> a {
display: block;
padding: 14px 30px;
color: var(--cp-dark);
}
}
}
}
渲染请求数据
TS类型
// 二级科室
export type SubDep = {
/** 科室ID */
id: string
/** 科室名称 */
name: string
}
// 一级科室
export type TopDep = SubDep & {
/** 二级科室数组 */
child: SubDep[]
}
接口
URL:
/dep/all
类型:
GET
token:携带
参数:无
返回数据:
渲染数据
1、在 services/consult.ts 中发送网络请求
2、在组件中调用请求方法,获取一级科室列表数据
3、渲染一级科室数据
4、根据 active 属性,获取二级科室列表数据
5、渲染二级科室数据
记录问诊科室
1、在 store/consult.ts 中定义 setDep() 方法,记录问诊科室
2、在 consult-department 组件调用 setDep() 方法,记录问诊科室
病情描述:consult-illness
路由规则
页面布局
1、HTML
<template>
<div class="consult-illness-page">
<cp-nav-bar title="图文问诊" />
<!-- 医生提示 -->
<div class="illness-tip van-hairline--bottom">
<img class="img" src="@/assets/avatar-doctor.svg" />
<div class="info">
<p class="tit">在线医生</p>
<p class="tip">
请描述你的疾病或症状、是否用药、就诊经历,需要我听过什么样的帮助
</p>
<p class="safe">
<cp-icon name="consult-safe" /><span>内容仅医生可见</span>
</p>
</div>
</div>
<!-- 收集信息 -->
<div class="illness-form">
<van-field
type="textarea"
rows="3"
placeholder="请详细描述您的病情,病情描述不能为空"
></van-field>
<div class="item">
<p>本次患病多久了?</p>
</div>
<div class="item">
<p>此次病情是否去医院就诊过?</p>
</div>
</div>
</div>
</template>
2、样式
.consult-illness-page {
padding-top: 46px;
}
.illness-tip {
display: flex;
padding: 15px;
.img {
width: 52px;
height: 52px;
border-radius: 4px;
overflow: hidden;
margin-top: 10px;
}
.info {
flex: 1;
padding-left: 12px;
.tit {
font-size: 16px;
margin-bottom: 5px;
}
.tip {
padding: 12px;
background: var(--cp-bg);
color: var(--cp-text3);
font-size: 13px;
margin-bottom: 10px;
}
.safe {
font-size: 10px;
color: var(--cp-text3);
display: flex;
align-items: center;
.cp-icon {
font-size: 12px;
margin-right: 2px;
}
}
}
}
.illness-form {
padding: 15px;
.van-field {
padding: 0;
&::after {
border-bottom: none;
}
}
.item {
> p {
color: var(--cp-text3);
padding: 15px 0;
}
}
}
表单数据
1、TS类型
export type ConsultIllness = Pick<
PartialConsult,
'illnessDesc' | 'illnessTime' | 'consultFlag' | 'pictures'
>
2、表单数据
import type { ConsultIllness } from '@/types/consult'
import { ref } from 'vue'
import { IllnessTime } from '@/enums'
const timeOptions = [
{ label: '一周内', value: IllnessTime.Week },
{ label: '一月内', value: IllnessTime.Month },
{ label: '半年内', value: IllnessTime.HalfYear },
{ label: '半年以上', value: IllnessTime.More }
]
const flagOptions = [
{ label: '就诊过', value: 1 },
{ label: '没就诊过', value: 0 }
]
const form = ref<ConsultIllness>({
illnessDesc: '',
illnessTime: undefined,
consultFlag: undefined,
pictures: []
})
渲染数据
上传图片@
页面布局
1、HTML
<div class="illness-img">
<van-uploader></van-uploader>
<p class="tip" >上传内容仅医生可见,最多9张图,最大5MB</p>
</div>
2、样式
.illness-img {
padding-top: 16px;
margin-bottom: 40px;
display: flex;
align-items: center;
.tip {
font-size: 12px;
color: var(--cp-tip);
}
::v-deep() {
.van-uploader {
&__preview {
&-delete {
left: -6px;
top: -6px;
border-radius: 50%;
background-color: var(--cp-primary);
width: 20px;
height: 20px;
&-icon {
transform: scale(0.9) translate(-22%, 22%);
}
}
&-image {
border-radius: 8px;
overflow: hidden;
}
}
&__upload {
border-radius: 8px;
}
&__upload-icon {
color: var(--cp-text3);
}
}
}
}
配置图片文字
限制数量和大小
收集图片数据
实现上传业务
接口
URL:
/upload
类型:
POST
token:携带
参数:
ts{ file: FormData // 上传的文件 }
返回数据:
上传图片
1、在 services/consult.ts 中发送网络请求
2、在组件中,通过after-read
绑定图片上传完毕后的回调函数
3、通过after-read
的item.status参数定义上传状态
4、同步上传数据到form.pictures中
3、在组件中,通过@delete
监听删除文件预览的处理函数
记录病情描述
按钮激活
1、默认情况下按钮有disabled类,此时按钮为灰色状态
2、样式默认为灰色
.van-button {
font-size: 16px;
margin-bottom: 30px;
&.disabled {
opacity: 1;
background: #fafafa;
color: #d9dbde;
border: #fafafa;
}
}
3、激活:当病情描述、时间、是否就诊选中时激活按钮
轻提示校验
记录病情描述
1、在 store/consult.ts 中定义记录病情描述的方法 setIllness()
2、在组件中调用 setIllness() 方法,记录病情描述,并跳转到选择患者页面
回显数据
1、当进入到病情描述页面时,判断是否已经存在病情描述数据,如果存在则回显该数据。
2、通过设置closeOnPopstate: false
处理从选择患者页面回退时,无法弹出弹框的问题
选择患者:patient
界面兼容
依据isChange=1
区分选择患者、家庭档案
选中患者效果
1、在患者项上绑定点击事件,设置选中患者id
2、阻止点击编辑按钮时的事件冒泡
默认选中
思路:进入页面后,如果有默认患者,则选中该患者;如果没有默认患者,则选中第一个患者。
记录患者ID
1、在 store/consult.ts 中,添加记录患者ID的方法 setPatient()
2、在组件中点击下一步时,调用 setPatient() 方法记录患者ID
问诊支付:consult-pay
路由规则
页面布局
1、HTML
<template>
<div class="consult-pay-page">
<cp-nav-bar title="支付" />
<div class="pay-info">
<p class="tit">图文问诊 49 元</p>
<img class="img" src="@/assets/avatar-doctor.svg" />
<p class="desc">
<span>极速问诊</span>
<span>自动分配医生</span>
</p>
</div>
<van-cell-group>
<van-cell title="优惠券" value="-¥10.00" />
<van-cell title="积分抵扣" value="-¥10.00" />
<van-cell title="实付款" value="¥29.00" class="pay-price" />
</van-cell-group>
<div class="pay-space"></div>
<van-cell-group>
<van-cell title="患者信息" value="李富贵 | 男 | 30岁"></van-cell>
<van-cell title="病情描述" label="头痛,头晕,恶心"></van-cell>
</van-cell-group>
<div class="pay-schema">
<van-checkbox>我已同意 <span class="text">支付协议</span></van-checkbox>
</div>
<van-submit-bar button-type="primary" :price="2900" button-text="立即支付" text-align="left" />
</div>
</template>
2、样式
.consult-pay-page {
padding: 46px 0 50px 0;
}
.pay-info {
display: flex;
padding: 15px;
flex-wrap: wrap;
align-items: center;
.tit {
width: 100%;
font-size: 16px;
margin-bottom: 10px;
}
.img {
margin-right: 10px;
width: 38px;
height: 38px;
border-radius: 4px;
overflow: hidden;
}
.desc {
flex: 1;
> span {
display: block;
color: var(--cp-tag);
&:first-child {
font-size: 16px;
color: var(--cp-text2);
}
}
}
}
.pay-price {
::v-deep() {
.vam-cell__title {
font-size: 16px;
}
.van-cell__value {
font-size: 16px;
color: var(--cp-price);
}
}
}
.pay-space {
height: 12px;
background-color: var(--cp-bg);
}
.pay-schema {
height: 56px;
display: flex;
align-items: center;
justify-content: center;
.text {
color: var(--cp-primary);
}
}
::v-deep() {
.van-submit-bar__button {
font-weight: normal;
width: 160px;
}
}
.pay-type {
.amount {
padding: 20px;
text-align: center;
font-size: 16px;
font-weight: bold;
}
.btn {
padding: 15px;
}
.van-cell {
align-items: center;
.cp-icon {
margin-right: 10px;
font-size: 18px;
}
.van-checkbox :deep(.van-checkbox__icon) {
font-size: 16px;
}
}
}
渲染请求数据
TS类型
// 问诊订单预支付传参
export type ConsultOrderPreParams = Pick<PartialConsult, 'type' | 'illnessType'>
// 问诊订单预支付信息
export type ConsultOrderPreData = {
/** 积分抵扣 */
pointDeduction: number
/** 优惠券抵扣 */
couponDeduction: number
/** 优惠券ID */
couponId: string
/** 需付款 */
payment: number
/** 实付款 */
actualPayment: number
}
接口-预支付信息
URL:
/patient/consult/order/pre
类型:
GET
token:携带
参数:
ts{ // 急速问诊只需要以下2个属性 type: string // 问诊类型:1 找医生,2 极速问诊,3 开药问诊 illnessType: string // 极速问诊级别:0 普通,1 三甲 useCoupon: string // 是否使用优惠券:0 不使用优惠券,1 使用优惠券(默认,且按最大优惠券使用)。 couponId: string // 可使用优惠券id docId: string // 医生id,当type为1时必传 }
返回数据:
接口-患者信息
URL:
/patient/info/:id
类型:
GET
token:携带
参数:
ts{ id: string // 患者id }
返回数据:
渲染数据
1、在 services/consult.ts 中发送网络请求
2、在 services/patient.ts 中发送网络请求
3、在组件中,调用请求方法
4、渲染数据
记录优惠券
1、在store中定义 setCoupon() 方法记录优惠券
2、在组件中,获取到预支付信息后,调用 setCoupon() 方法记录优惠券
骨架屏@
实现支付
支付流程
生成订单
支付抽屉面板
1、在 van-submit-bar 上绑定点击事件,打开支付抽屉面板
2、支付抽屉面板布局
接口
URL:
/patient/consult/order
类型:
POST
token:携带
参数:
tsConsult
返回数据:
实现生成订单
1、选择支付方式
2、在 services/consult.ts 中发送网络请求
3、在组件中调用请求方法,获取订单id,同时添加loading效果
清理问诊数据
1、在store中定义 clear() 方法,清理问诊数据
2、在组件中调用 clear() 方法,清理问诊数据
用户引导
阻止返回上页
阻止关闭抽屉
1、设置属性阻止关闭抽屉
2、监听用户点击灰色区域关闭抽屉,弹出确认弹框
刷新页面提示
思路:刷新页面后在onMounted钩子中校验所有需要的key对应的值是否为undefined,如果所有值都不是undefined,则校验通过,否则弹出提示框,提示数据不完整。
实现支付
接口
URL:
/patient/consult/pay
类型:
POST
token:携带
参数:
ts{ paymentMethod: string // 支付方式:0 微信支付,1 支付宝,2 云闪付 orderId: string // 订单id payCallback: string // 回跳地址,http://域名/回跳页面 }
返回数据:
实现支付
1、在 services/consult.ts 中发送网络请求
2、在组件中调用请求方法,获取支付地址
处理支付失败
思路:支付完成后等待几秒会跳转到指定地址,url参数中包括支付结果payResult,支付成功payResult为true,支付失败payResult为false。
在room路由的beforeEnter路由钩子中处理支付失败的跳转